<?php
/**
 * Pertyler
 * Database Model
 * Table Classes
 *
 * @author	Emre Hasegeli <hasegeli@gmail.com>
 * @license	GNU General Public License v3
 */

namespace Pertyler;

class Pertyler_Table
{
	private static $object;

	protected $parent_id;

	protected $id;

	protected $name;

	protected $shortName;

	protected $backgroundName;

	protected $schema;

	protected $hasNameTable;

	protected $hasNameTableAlternativeColumn;

	protected $hasLanguage;

	protected $hasParentsName;

	protected $tableType;

	protected function __construct($parent_id,
			$id,
			$name,
			$shortName,
			$backgroundName,
			$schema,
			$hasNameTable,
			$hasNameTableAlternativeColumn,
			$hasLanguage,
			$hasParentsName,
			$tableType)
	{
		$this->parent_id = $parent_id;

		$this->id = $id;

		$this->name = $name;

		$this->shortName = $shortName;

		$this->backgroundName = $backgroundName;

		$this->schema = $schema;

		$this->hasNameTable = $hasNameTable;

		$this->hasNameTableAlternativeColumn = $hasNameTableAlternativeColumn;

		$this->hasLanguage = $hasLanguage;

		$this->hasParentsName = $hasParentsName;

		$this->tableType = $tableType;

		self::$object[$id] = $this;
	}

	public function Id()
	{
		return $this->id;
	}

	public function __ToString()
	{
		return $this->name;
	}

	public function Schema()
	{
		return $this->schema;
	}

	public function ConnectorTable()
	{
		$nameArray = explode('_',
				$this->backgroundName);

		$id = 0;

		while((isset($nameArray[$id + 1])))
			$id++;

		if(ucfirst($nameArray[$id]) == $nameArray[$id])
			return false;

		if($nameArray[$id] == 'name')
			return false;

		return true;
	}

	public function NameTable()
	{
		$nameArray = explode('_',
				$this->backgroundName);

		$id = 0;

		while((isset($nameArray[$id + 1])))
			$id++;

		if($nameArray[$id] == 'name')
			return true;

		return false;
	}

	public function uniqueName()
	{
		$nameArray = explode('_',
				$this->backgroundName);

		$name = '';

		$id = 0;

		while((isset($nameArray[$id])))
		{
			if(!is_numeric($nameArray[$id]))
				if(ucfirst($nameArray[$id]) == $nameArray[$id])
					$name = $nameArray[$id];
				else
					$name .= ucfirst($nameArray[$id]);

			$id++;
		}

		return $name;
	}

	public function backgroundName()
	{
		return $this->backgroundName;
	}

	public function TableType()
	{
		return $this->tableType;
	}

	public function FileTable()
	{
		return (($this->tableType == 'file') OR ($this->tableType == 'imageFile') OR ($this->tableType == 'documentFile'));
	}

	public static function Select($id)
	{
		if(!isset(self::$object[$id]))
		{
			$resultArray = Database::Postgre()->SelectResultArray('
SELECT "Table"."Table",
		"Table"."Schema",
		"TableName"."Name",
		"TableName"."ShortName",
		"Table"."BackgroundName",
		EXISTS(SELECT true
				FROM information_schema.tables
				WHERE table_schema = "Table"."Schema"
						AND table_name = regexp_replace("Table"."BackgroundName",
								\'.*[_]\', \'\') || \'_name\') "HasNameTable",
		EXISTS(SELECT true
				FROM information_schema.columns
				WHERE table_schema = "Table"."Schema"
						AND table_name = regexp_replace("Table"."BackgroundName",
								\'.*[_]\', \'\') || \'_name\' AND column_name = \'Alternative\') "HasNameTableAlternativeColumn",
		"Table"."HasLanguage",
		"Table"."HasParentsName",
		"TableType"."Name" "TableType_Name"
	FROM pertyler."Table_Table" "Table"
		JOIN pertyler."Table_name" "TableName" ON "Table".id = "TableName"."Table"
				AND "TableName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		LEFT JOIN pertyler."TableType" ON "Table"."TableType" = "TableType".id
	WHERE "Table".id = ' . $id);

			if($resultArray)
				new Pertyler_Table($resultArray['Table'],
						$id,
						$resultArray['Name'],
						$resultArray['ShortName'],
						$resultArray['BackgroundName'],
						$resultArray['Schema'],
						$resultArray['HasNameTable'],
						$resultArray['HasNameTableAlternativeColumn'],
						$resultArray['HasLanguage'],
						$resultArray['HasParentsName'],
						$resultArray['TableType_Name']);
		}

		if(isset(self::$object[$id]))
			return self::$object[$id];

		return NULL;
	}

	public function SelectParentTable()
	{
		if($this->parent_id)
			if(self::Select($this->parent_id))
				return self::Select($this->parent_id);
			else
				return parent::Select($this->parent_id);
		else
			return NULL;
	}

	public function RowsTable(Pertyler_Table_Row $row)
	{
		return new Pertyler_Row_Table($row,
				$this->parent_id,
				$this->id,
				$this->name,
				$this->shortName,
				$this->backgroundName,
				$this->schema,
				$this->hasNameTable,
				$this->hasNameTableAlternativeColumn,
				$this->hasLanguage,
				$this->hasParentsName,
				$this->tableType);
	}

	public static function SelectTopLevelTableArray()
	{
		$query = '
SELECT "Table".id,
		"TableName"."Name",
		"TableName"."ShortName",
		"Table"."BackgroundName",
		"Table"."Schema",
		EXISTS(SELECT TRUE FROM information_schema.tables WHERE table_schema = "Table"."Schema" AND table_name = regexp_replace("Table"."BackgroundName", \'.*[_]\', \'\') || \'_name\') "HasNameTable",
		EXISTS(SELECT TRUE FROM information_schema.columns WHERE table_schema = "Table"."Schema" AND table_name = regexp_replace("Table"."BackgroundName", \'.*[_]\', \'\') || \'_name\' AND column_name = \'Alternative\') "HasNameTableAlternativeColumn",
		"Table"."HasLanguage",
		"Table"."HasParentsName",
		"TableType"."Name" "TableType_Name"
	FROM "pertyler"."Table_Table" "Table"
		JOIN pertyler."Table_name" "TableName" ON "Table".id = "TableName"."Table"
				AND "TableName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		LEFT JOIN pertyler."TableType" ON "Table"."TableType" = "TableType".id
	WHERE "Table"."Table" IS NULL
			AND "Table"."BackgroundName" !~~ \'%Type\'
	ORDER BY "TableName"."Name"';

		$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);

		$tableArray = array();

		foreach($resultArrayArray AS $resultArray)
			if(isset(self::$object[$resultArray['id']]))
				$tableArray[] = self::$object[$resultArray['id']];
			else
				$tableArray[] = new Pertyler_Table(NULL,
						$resultArray['id'],
						$resultArray['Name'],
						$resultArray['ShortName'],
						$resultArray['BackgroundName'],
						$resultArray['Schema'],
						$resultArray['HasNameTable'],
						$resultArray['HasNameTableAlternativeColumn'],
						$resultArray['HasLanguage'],
						$resultArray['HasParentsName'],
						$resultArray['TableType_Name']);

		return $tableArray;
	}

	public function SelectTypeTable()
	{
		$query = '
SELECT "Table".id,
		"TableName"."Name",
		"TableName"."ShortName",
		"Table"."BackgroundName",
		"Table"."Schema",
		EXISTS(SELECT TRUE FROM information_schema.tables WHERE table_schema = "Table"."Schema" AND table_name = regexp_replace("Table"."BackgroundName", \'.*[_]\', \'\') || \'_name\') "HasNameTable",
		EXISTS(SELECT TRUE FROM information_schema.columns WHERE table_schema = "Table"."Schema" AND table_name = regexp_replace("Table"."BackgroundName", \'.*[_]\', \'\') || \'_name\' AND column_name = \'Alternative\') "HasNameTableAlternativeColumn",
		"Table"."HasLanguage",
		"Table"."HasParentsName",
		"TableType"."Name" "TableType_Name"
	FROM "pertyler"."Table_Table" "Table"
		JOIN pertyler."Table_name" "TableName" ON "Table".id = "TableName"."Table"
				AND "TableName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		LEFT JOIN pertyler."TableType" ON "Table"."TableType" = "TableType".id
	WHERE "Table"."BackgroundName" = \'' . $this->uniqueName() . 'Type\'';

		$resultArray = Database::Postgre()->SelectResultArray($query);

		if($resultArray)
			return new Pertyler_Table(NULL,
					$resultArray['id'],
					$resultArray['Name'],
					$resultArray['ShortName'],
					$resultArray['BackgroundName'],
					$resultArray['Schema'],
					$resultArray['HasNameTable'],
					$resultArray['HasNameTableAlternativeColumn'],
					$resultArray['HasLanguage'],
					$resultArray['HasParentsName'],
					$resultArray['TableType_Name']);

		return NULL;
	}

	private $tableArray;

	public function SelectTableArray()
	{
		if(!isset($this->tableArray))
		{
			$this->tableArray = array();

			$query = '
SELECT "Table".id,
		"TableName"."Name",
		"TableName"."ShortName",
		"Table"."BackgroundName",
		"Table"."Schema",
		EXISTS(SELECT TRUE FROM information_schema.tables WHERE table_schema = "Table"."Schema" AND table_name = regexp_replace("Table"."BackgroundName", \'.*[_]\', \'\') || \'_name\') "HasNameTable",
		EXISTS(SELECT TRUE FROM information_schema.columns WHERE table_schema = "Table"."Schema" AND table_name = regexp_replace("Table"."BackgroundName", \'.*[_]\', \'\') || \'_name\' AND column_name = \'Alternative\') "HasNameTableAlternativeColumn",
		"Table"."HasLanguage",
		"Table"."HasParentsName",
		"TableType"."Name" "TableType_Name"
	FROM pertyler."Table_Table" "Table"
		JOIN pertyler."Table_name" "TableName" ON "Table".id = "TableName"."Table"
				AND "TableName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		LEFT JOIN pertyler."TableType" ON "Table"."TableType" = "TableType".id
	WHERE "Table"."Table" = ' . $this->id . '
	ORDER BY "TableName"."Name"';

			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);

			foreach($resultArrayArray AS $resultArray)
				if(isset(self::$object[$resultArray['id']]))
					$this->tableArray[] = self::$object[$resultArray['id']];
				else
					$this->tableArray[] = new Pertyler_Table($this->id,
							$resultArray['id'],
							$resultArray['Name'],
							$resultArray['ShortName'],
							$resultArray['BackgroundName'],
							$resultArray['Schema'],
							$resultArray['HasNameTable'],
							$resultArray['HasNameTableAlternativeColumn'],
							$resultArray['HasLanguage'],
							$resultArray['HasParentsName'],
							$resultArray['TableType_Name']);
		}

		return $this->tableArray;
	}

	public function SelectRowsTableArray(Pertyler_Table_Row $row)
	{
		$tableArray = array();

		foreach($this->SelectTableArray() AS $table)
			$tableArray[] = new Pertyler_Row_Table($row,
					$table->parent_id,
					$table->id,
					$table->name,
					$table->shortName,
					$table->backgroundName,
					$table->schema,
					$table->hasNameTable,
					$table->hasNameTableAlternativeColumn,
					$table->hasLanguage,
					$table->hasParentsName,
					$table->tableType);

		return $tableArray;
	}

	private $firstColumn;

	public function SelectFirstColumn()
	{
		if(!isset($this->firstColumn))
		{
			if($this->hasNameTable)
				$this->firstColumn = new Pertyler_Table_Column($this, 1, 'Name', 'Name', 'string', NULL);
			else
			{
				$query = '
SELECT "Column".id, "ColumnName"."Name", "Column"."BackgroundName", "ColumnType"."Name" "ColumnType", "Column"."RelatedTable"
	FROM "pertyler"."Table_Column" "Column"
		JOIN "pertyler"."Column_name" "ColumnName" ON "Column".id = "ColumnName"."Column"
				AND "ColumnName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		LEFT JOIN pertyler."ColumnType" ON "Column"."ColumnType" = "ColumnType".id
	WHERE "Column"."Table" = ' . $this->id . '
			AND "Column"."Priority" = 1';

				$resultArray = Database::Postgre()->SelectResultArray($query);

				if($resultArray)
				{
					if(isset($resultArray['RelatedTable']))
						$relatedTable = Pertyler_Table::Select($resultArray['RelatedTable']);
					else
						$relatedTable = NULL;

					$this->firstColumn = new Pertyler_Table_Column($this, 1, $resultArray['Name'], $resultArray['BackgroundName'], $resultArray['ColumnType'], $relatedTable);
				}
				else
					throw new Exception('Table ' . $this . ' has no name column');
			}
		}

		return $this->firstColumn;
	}

	public function SelectColumn($priority)
	{
		if($priority == 1)
			return $this->SelectFirstColumn();
		else
		{
			if(!isset($this->otherColumnArray))
				$this->SelectColumnArray();

			if(isset($this->otherColumnArray[$priority]))
				return $this->otherColumnArray[$priority];
			else
				return NULL;
		}
	}

	private $otherColumnArray;

	public function SelectColumnArray()
	{
		if(!isset($this->otherColumnArray))
		{
			$this->otherColumnArray = array();

			$query = '
SELECT "Column".id,
		"ColumnName"."Name",
		"Column"."BackgroundName",
		"ColumnType"."Name" "ColumnType",
		"Column"."RelatedTable",
		"Column"."Priority"
	FROM "pertyler"."Table_Column" "Column"
		JOIN "pertyler"."Column_name" "ColumnName" ON "Column".id = "ColumnName"."Column"
				AND "ColumnName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		LEFT JOIN pertyler."ColumnType" ON "Column"."ColumnType" = "ColumnType".id
	WHERE "Column"."Table" = ' . $this->id . '
			AND "Column"."Priority" > 1
			AND ("ColumnType".id IS NULL OR "ColumnType"."Name" != \'group\')
	ORDER BY "Column"."Priority"';

			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);

			foreach($resultArrayArray AS $resultArray)
			{
				if(isset($resultArray['RelatedTable']))
					$relatedTable = Pertyler_Table::Select($resultArray['RelatedTable']);
				else
					$relatedTable = NULL;

				$this->otherColumnArray[$resultArray['Priority']] = new Pertyler_Table_Column($this, $resultArray['Priority'], $resultArray['Name'], $resultArray['BackgroundName'], $resultArray['ColumnType'], $relatedTable);
			}
		}

		$columnArray[1] = $this->SelectFirstColumn();

		$columnArray += $this->otherColumnArray;

		return $columnArray;
	}

	private $groupColumnArray;

	public function SelectGroupColumnArray()
	{
		if(empty($this->groupColumnArray))
		{
			$this->groupColumnArray = array();

			$query = '
SELECT "Column".id, "ColumnName"."Name", "Column"."BackgroundName", "Column"."Priority"
	FROM "pertyler"."Table_Column" "Column"
		JOIN "pertyler"."Column_name" "ColumnName" ON "Column".id = "ColumnName"."Column"
				AND "ColumnName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
		INNER JOIN pertyler."ColumnType" ON "Column"."ColumnType" = "ColumnType".id
				AND "ColumnType"."Name" = \'group\'
	WHERE "Column"."Table" = ' . $this->id . '
	ORDER BY "Column"."Priority"';

			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);

			if($resultArrayArray)
				foreach($resultArrayArray AS $resultArray)
					$this->groupColumnArray[$resultArray['Priority']] = new Pertyler_Table_Column($this, $resultArray['Priority'], $resultArray['Name'], $resultArray['BackgroundName'], 'group', NULL);

		}

		return $this->groupColumnArray;
	}

	private $relatedColumnArray;

	public function SelectRelatedColumnArray()
	{
		if(!isset($this->relatedColumnArray))
		{
			$this->relatedColumnArray = array();

			$resultArrayArray = Database::Postgre()->SelectResultArrayArray('
SELECT "Column"."Table", "Column".id, "ColumnName"."Name", "Column"."BackgroundName", "ColumnType"."Name" "ColumnType", "Column"."Priority"
FROM pertyler."Table_Column" "Column"
	JOIN "pertyler"."Column_name" "ColumnName" ON "Column".id = "ColumnName"."Column"
			AND "ColumnName"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id() . '
	LEFT JOIN pertyler."ColumnType" ON "Column"."ColumnType" = "ColumnType".id
WHERE "Column"."RelatedTable" = ' . $this->id . '
ORDER BY "Column"."Priority"');

			foreach($resultArrayArray AS $resultArray)
			{
				$table = Pertyler_Table::Select($resultArray['Table']);

				if((($table->SelectParentTable()) AND ($table->SelectParentTable()->id != $this->id)) OR (!$table->ConnectorTable()))
					$this->relatedColumnArray[] = new Pertyler_Table_Column($table, $resultArray['Priority'], $resultArray['Name'], $resultArray['BackgroundName'], $resultArray['ColumnType'], $this);
			}
		}

		return $this->relatedColumnArray;
	}

	private $privilegeArrayArray;

	public function SelectPrivilege($privilegeType, Pertyler_Language $language = NULL)
	{
		if(!isset($this->privilegeArrayArray))
			$this->SelectPrivilegeArrayArray();

		if($language)
		{
			if(isset($this->privilegeArrayArray[$privilegeType][$language->id()]))
				return $this->privilegeArrayArray[$privilegeType][$language->id()];
		}
		else
			if(isset($this->privilegeArrayArray[$privilegeType][Pertyler_Role::Select()->DefaultLanguage()->id()]))
				return $this->privilegeArrayArray[$privilegeType][Pertyler_Role::Select()->DefaultLanguage()->id()];
			else
				if(isset($this->privilegeArrayArray[$privilegeType]))
					return current($this->privilegeArrayArray[$privilegeType]);

		return NULL;
	}

	public function SelectPrivilegeArray($privilegeType = NULL)
	{
		if(!isset($this->privilegeArrayArray))
			$this->SelectPrivilegeArrayArray();

		if($privilegeType)
			if(isset($this->privilegeArrayArray[$privilegeType]))
				return $this->privilegeArrayArray[$privilegeType];
			else
				return array();
		else
			return $this->privilegeArrayArray;
	}

	private function selectPrivilegeArrayArray()
	{
		if(!isset($this->privilegeArray))
		{
			$this->privilegeArray = array();

			$resultArrayArray = Database::Postgre()->SelectResultArrayArray('
SELECT "Privilege".id, "PrivilegeType"."Name" "PrivilegeType", "Privilege"."Language"
FROM pertyler."Table_Privilege" "Privilege"
	JOIN pertyler."PrivilegeType" ON "Privilege"."PrivilegeType" = "PrivilegeType".id
WHERE "Privilege"."Table" = ' . $this->id);

			if($resultArrayArray)
				foreach($resultArrayArray AS $resultArray)
					$this->privilegeArrayArray[$resultArray['PrivilegeType']][$resultArray['Language']] = new Pertyler_Table_Privilege($this, $resultArray['id'], $resultArray['PrivilegeType'], new Pertyler_Language($resultArray['Language']));
		}

		return $this->privilegeArrayArray;
	}

	protected function addParentsNameSql($prefix = '')
	{
		$parentTable = $this->SelectParentTable();

		$query = '';

		if($parentTable->hasParentsName)
			$query .= $parentTable->addParentsNameSql($prefix);

		if($parentTable->hasNameTable)
			$query .= '"' . $prefix . $parentTable->uniqueName() . 'Name"."Name"';
		else
			$query .= '"' . $prefix . $parentTable->uniqueName() . '"."' . $parentTable->SelectColumn(1)->backgroundName() . '"';

		$query .= " || ' ' || ";

		return $query;
	}

	protected function nameSelectSql()
	{
		$query = '';

		if($this->hasParentsName)
			$query .= $this->addParentsNameSql();

		$prefix = '';

		if(!$this->SelectColumn(1))
			throw new Exception('Table ' . $this . ' has no name column.');

		$table = $this;

		while(($table->SelectColumn(1)->RelatedTable()) AND ($table->SelectColumn(1)->RelatedTable()->SelectColumn(1)))
		{
			$table = $table->SelectColumn(1)->RelatedTable();

			$prefix = 'Name_';

			if($table->hasParentsName)
				$query .= $table->addParentsNameSql($prefix);
		}

		if($table->hasNameTable)
			$query .= '"' . $prefix . $table->uniqueName() . 'Name"."Name"';
		else
		{
			$query .= '"' . $prefix . $table->uniqueName() . '"."' . $table->SelectColumn(1)->backgroundName() . '"::text';
		}

		return $query;
	}
	
	protected function selectClauseSql()
	{
		$query = '
SELECT "' . $this->uniqueName() . '".id, ';

		$query .= $this->nameSelectSql();

		if(($this->hasParentsName) OR ($this->SelectColumn(1)->backgroundName() != 'Name'))
			$query .= ' "Name"';
			
		return $query;
	}

	private function joinParentTableSql($unpublished,
			$prefix = '')
	{
		$parentTable = $this->SelectParentTable();

		$query = '
	JOIN "' . $parentTable->schema . '"."' . $parentTable->backgroundName() . '" "' . $prefix . $parentTable->uniqueName() . '"
			ON "' . $prefix . $this->uniqueName() . '"."' . $parentTable->uniqueName() . '" = "' . $prefix . $parentTable->uniqueName() . '".id';

		if(($unpublished) AND ($parentTable->SelectColumn(2)) AND ($parentTable->SelectColumn(2)->backgroundName() == 'Unpublished'))
			$query .= '
					AND "' . $prefix . $parentTable->uniqueName() . '"."Unpublished" = false';

		if(($this->hasParentsName) AND ($parentTable->hasNameTable))
		{
			$query .= '
		JOIN "' . $parentTable->schema . '"."' . $parentTable->uniqueName() . '_name" "' . $prefix . $parentTable->uniqueName() . 'Name"
				ON "' . $prefix . $parentTable->uniqueName() . '".id = "' . $prefix . $parentTable->uniqueName() . 'Name"."' . $parentTable->uniqueName() . '"
					AND "' . $prefix . $parentTable->uniqueName() . 'Name"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id();

			if($parentTable->hasNameTableAlternativeColumn)
				$query .= '
					AND "' . $prefix . $parentTable->uniqueName() . 'Name"."Alternative" = false';
		}

		if($parentTable->SelectParentTable())
			$query .= $parentTable->joinParentTableSql($unpublished,
					$prefix);

		return $query;
	}

	private function joinNameTableSql($prefix = '')
	{
		$nameColumn = $this->SelectColumn(1);

		$table = $nameColumn->RelatedTable();

		$query = '
JOIN "' . $table->schema . '"."' . $table->backgroundName() . '" "Name_' . $table->uniqueName() . '"
		ON "' . $prefix . $this->uniqueName() . '"."' . $nameColumn->backgroundName() . '" = "Name_' . $table->uniqueName() . '".id';

		if(($table->SelectColumn(2)) AND ($table->SelectColumn(2)->backgroundName() == 'Unpublished'))
			$query .= '
				AND "Name_' . $table->uniqueName() . '"."Unpublished" = false';

		if(($table->SelectColumn(1)) and ($table->SelectColumn(1)->RelatedTable()))
			$query .= $table->joinNameTableSql('Name_');
		else if($table->hasNameTable)
		{
			$query .= '
	JOIN "' . $table->schema . '"."' . $table->uniqueName() . '_name" "Name_' . $table->uniqueName() . 'Name"
			ON "Name_' . $table->uniqueName() . '".id = "Name_' . $table->uniqueName() . 'Name"."' . $table->uniqueName() . '"
				AND "Name_' . $table->uniqueName() . 'Name"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id();

			if($table->hasNameTableAlternativeColumn)
				$query .= '
				AND "Name_' . $table->uniqueName() . 'Name"."Alternative" = false';
		}

		if($table->hasParentsName)
			$query .= $table->joinParentTableSql(true,
					'Name_');

		return $query;
	}

	protected function fromClauseSql($unpublished)
	{
		$query = '
FROM "' . $this->schema . '"."' . $this->backgroundName() . '" "' . $this->uniqueName() . '"';

		if($this->hasNameTable)
		{
			$query .= '
	JOIN "' . $this->schema . '"."' . $this->uniqueName() . '_name" "' . $this->uniqueName() . 'Name"
			ON "' . $this->uniqueName() . '".id = "' . $this->uniqueName() . 'Name"."' . $this->uniqueName() . '"
				AND "' . $this->uniqueName() . 'Name"."Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id();

			if($this->hasNameTableAlternativeColumn)
				$query .= '
				AND "' . $this->uniqueName() . 'Name"."Alternative" = false';
		}				

		if($this->SelectParentTable())
			$query .= $this->joinParentTableSql($unpublished);

		if(($this->SelectColumn(1)) AND ($this->SelectColumn(1)->RelatedTable()))
			$query .= $this->joinNameTableSql();

		return $query;
	}

	protected function whereClauseLanguageCheckSql()
	{
		if($this->hasLanguage)
		{
			$privilegeArray = $this->SelectPrivilegeArray('select');

			$query = '';

			if(count($privilegeArray) > 1)
				$query .= '
(';

			$counter = 0;

			foreach($privilegeArray AS $privilege)
				if(Pertyler_Role::Select()->SelectRolePrivilege($privilege))
				{
					if($counter++)
						$query .= '
	OR ';
	
					$query .= '"' . $this->uniqueName() . '"."Language" = ' . $privilege->Language()->id();
				}

			if(count($privilegeArray) > 1)
				$query .= '
)';

			return $query;
		}
		else
			return 'true';
	}

	protected function whereClauseSql()
	{
		$query = '
WHERE ';

		$counter = 0;

		if(($this->SelectColumn(2)) AND ($this->SelectColumn(2)->backgroundName() == 'Unpublished'))
		{
			$counter++;
			
			$query .= '"' . $this->uniqueName() . '"."Unpublished" = false';
		}

		if($this->hasLanguage)
		{
			if($counter++)
				$query .= '
		AND ';

			$query .= $this->whereClauseLanguageCheckSql();
		}

		if(!$counter)
			$query .= 'true';

		return $query;
	}

	public function SelectRow($id)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No privilege for table ' . $this->id());

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$query = $this->selectClauseSql();

		if(($this->SelectColumn(2)) AND ($this->SelectColumn(2)->backgroundName() == 'Unpublished'))
			$query .= ', "' . $this->uniqueName() . '"."Unpublished"';

		$query .= $this->fromClauseSql(false);

		$query .= '
WHERE "' . $this->uniqueName() . '".id = ' . Database::Postgre()->SelectInput($id);

		$resultArray = Database::Postgre()->SelectResultArray($query);

		if($resultArray)
			if(($this->SelectColumn(2)) AND ($this->SelectColumn(2)->backgroundName() == 'Unpublished'))
				return new Pertyler_Table_Row($this, $resultArray['id'], $resultArray['Name'], $resultArray['Unpublished']);
			else
				return new Pertyler_Table_Row($this, $resultArray['id'], $resultArray['Name']);
		else
			return NULL;
	}

	public function SelectRowArray()
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilege . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$query = $this->selectClauseSql();

		$query .= $this->fromClauseSql(true);

		$query .= $this->whereClauseSql();

		$query .= '
ORDER BY "Name"';

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		$rowArray = array();

		if($resultArrayArray)
			foreach($resultArrayArray AS $resultArray)
				$rowArray[] = new Pertyler_Table_Row($this,
						$resultArray['id'],
						$resultArray['Name']);

		return $rowArray;
	}

	public function SelectUnpublishedRowArray()
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		if((!$this->SelectColumn(2)) OR ($this->SelectColumn(2)->backgroundName() != 'Unpublished'))
			throw new Exception('No unpublished column.');

		$rowArray = array();

		$query = $this->selectClauseSql();

		$query .= $this->fromClauseSql(false);

		$query .= '
WHERE "' . $this->uniqueName() . '"."Unpublished" = true
		AND ' . $this->whereClauseLanguageCheckSql();

		$query .= '
ORDER BY "Name"';

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this, $resultArray['id'], $resultArray['Name'], true);

		return $rowArray;
	}

	public function SelectUnapprovedRowArray()
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		if(!$this->SelectPrivilege('approve'))
			throw new Exception('No ' . $privilage . ' privilege');

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($this->selectClauseSql() . $this->fromClauseSql(false) . '
	WHERE ' . $this->whereClauseLanguageCheckSql() . '
			AND "' . $this->uniqueName() . '".id::text !~ \'[^0-9]\'
			AND NOT EXISTS(SELECT true
					FROM pertyler."Table_ChangeLog"
					WHERE id  = (SELECT max(id)
							FROM pertyler."Table_ChangeLog"
							WHERE "Table" = ' . $this->id . '
									AND "Row" = "' . $this->uniqueName() . '".id::int)
							AND "PrivilegeType" = 4)
	ORDER BY EXISTS(SELECT true
			FROM pertyler."Table_ChangeLog"
			WHERE "Table" = ' . $this->id . '
					AND "Row" = "' . $this->uniqueName() . '".id::int
					AND "PrivilegeType" = 4),
			"Name"');
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		$rowArray = array();

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name']);

		return $rowArray;
	}

	public function SelectRelatedRowArray(Pertyler_Table_Row $row, Pertyler_Table_Column $column)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($this->selectClauseSql() . $this->fromClauseSql(true) . $this->whereClauseSql() . '
		AND "' . $this->uniqueName() . '"."' . $column->backgroundName() . '" = ' . $row->id() . '
ORDER BY "Name"');
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		$rows = array();

		foreach($resultArrayArray AS $resultArray)
			$rows[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name']);

		return $rows;
	}

	public function SelectRowsParentRow(Pertyler_Table_Row $row)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		try
		{
			$resultArray = Database::Postgre()->SelectResultArray($this->SelectParentTable()->selectClauseSql() . $this->SelectParentTable()->fromClauseSql(false) . '
JOIN "' . $this->schema . '"."' . $this->backgroundName . '" "child_' . $this->uniqueName() . '"
		ON "' . $this->SelectParentTable()->uniqueName() . '".id = "child_' . $this->uniqueName() . '"."' . $this->SelectParentTable()->uniqueName() . '"
		AND "child_' . $this->uniqueName() . '".id = ' . Database::Postgre()->SelectInput($row->id()));
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		$rows = array();

		if($resultArray)
			return new Pertyler_Table_Row($this->SelectParentTable(),
					$resultArray['id'],
					$resultArray['Name']);
	}

	public function SelectSearchedRowArray(Array $inputColumnArray)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$query = $this->selectClauseSql();

		$query .= $this->fromClauseSql(true);

		$query .= $this->whereClauseSql();

		$searchedColumn = 0;

		$query .= '
AND
(';

		foreach($this->SelectColumnArray() AS $column)
			if(($column->Priority() == 1) AND (isset($inputColumnArray[$column->Priority()])) AND ($inputColumnArray[$column->Priority()]))
			{
				if($searchedColumn++)
					$query .= '
	OR';

				$query .= '
		' . $this->nameSelectSql() . ' ~~* ' . Database::Postgre()->SelectInput('%' . trim($inputColumnArray[$column->Priority()]) . '%');
			}
			elseif(($column->ColumnType() == 'string') AND (isset($inputColumnArray[$column->Priority()])) AND ($inputColumnArray[$column->Priority()]))
			{
				if($searchedColumn++)
					$query .= '
	OR';

				$query .= '
		"' . $this->uniqueName() . '"."' . $column->backgroundName() . '" ~~* ' . Database::Postgre()->SelectInput('%' . trim($inputColumnArray[$column->Priority()]) . '%');
			}
			elseif((($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph')) AND (isset($inputColumnArray[$column->Priority()])) AND ($inputColumnArray[$column->Priority()]))
			{
				if($searchedColumn++)
					$query .= '
	OR';

				$query .= '
		"' . $this->uniqueName() . '"."' . $column->backgroundName() . '"::text ~~* ' . Database::Postgre()->SelectInput('%' . trim($inputColumnArray[$column->Priority()]) . '%');
			}

		$query .= '
)
ORDER BY "Name"';

		$rowArray = array();

		if(!$searchedColumn)
			return $rowArray;

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name']);

		return $rowArray;
	}

	public function SelectRowsGroupRowArray(Pertyler_Table_Row $row,
			Pertyler_Row_Column $column)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$rowArray = array();

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($this->selectClauseSql() . $this->fromClauseSql(true) . $this->whereClauseSql() . '
		AND "' . $this->uniqueName() . '".id != ' . $row->id() . '
		AND	"' . $this->uniqueName() . '"."' . $column->backgroundName() . '" = ' . $column->Value() . '
ORDER BY "Name"');
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this->uniqueName() . '"');
		}

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name']);

		return $rowArray;
	}

	public function SelectGrouplessRowArray(Pertyler_Row_Column $column)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$rowArray = array();

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($this->selectClauseSql() . $this->fromClauseSql(true) . $this->whereClauseSql() . '
		AND	"' . $this->uniqueName() . '"."' . $column->backgroundName() . '" IS NULL
ORDER BY "Name"');
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this . '"');
		}

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name']);

		return $rowArray;
	}

	public function SelectNonexistentRowArray(Pertyler_Row_Table $table)
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$query = $this->selectClauseSql();

		$query .= $this->fromClauseSql(true);

		$query .= '
		LEFT JOIN "' . $table->schema . '"."' . $table->backgroundName . '" "' . $table->uniqueName() . '"
				ON "' . $this->uniqueName() . '".id = "' . $table->uniqueName() . '"."' . $this->uniqueName() . '"
				AND "' . $table->uniqueName() . '"."' . $table->SelectParentTable()->uniqueName() . '" = ' . $table->Parent()->id();

		$query .= $this->whereClauseSql();

		$query .= '
		AND	"' . $table->uniqueName() . '".id IS NULL
ORDER BY "Name"';

		$rowArray = array();

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);
		}
		catch(PostgreException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this . '"');
		}

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name']);

		return $rowArray;
	}

	public function SelectRowsColumnArray(Pertyler_Table_Row $row)
	{
		$columnArray = $this->SelectColumnArray();

		$resultColumnArray = array();

		if($this->hasNameTable)
		{
			$resultColumnArray[1] = $columnArray[1]->RowsColumn($row->__toString());

			unset($columnArray[1]);
		}

		if($columnArray)
		{
			$id = 0;

			foreach($columnArray AS $column)
			{
				if($id++)
					$query .= ', ';
				else
					$query = '
SELECT ';

				$query .= '"' . $column->backgroundName() . '"';
			}

			$query .= '
FROM "' . $this->schema . '"."' . $this->backgroundName() . '"
WHERE id = ' . Database::Postgre()->SelectInput($row->id());

			$resultArray = Database::Postgre()->SelectResultArray($query);

			foreach($columnArray AS $column)
				if(isset($resultArray[$column->BackgroundName()]))
					if($column->RelatedTable())
						$resultColumnArray[$column->Priority()] = $column->RowsColumn($column->RelatedTable()->SelectRow($resultArray[$column->BackgroundName()]));
					else
						$resultColumnArray[$column->Priority()] = $column->RowsColumn($resultArray[$column->BackgroundName()]);
				else
					$resultColumnArray[$column->Priority()] = $column->RowsColumn(NULL);
		}

		return $resultColumnArray;
	}

	public function SelectRowsGroupColumnArray(Pertyler_Table_Row $row)
	{
		$columnArray = $this->SelectGroupColumnArray();

		$resultColumnArray = array();

		if($columnArray)
		{
			$id = 0;

			foreach($columnArray AS $column)
			{
				if($id++)
					$query .= ', ';
				else
					$query = '
SELECT ';

				$query .= '"' . $column->backgroundName() . '"';
			}

			$query .= '
FROM "' . $this->schema . '"."' . $this->backgroundName() . '"
WHERE id = ' . $row->id();

			$resultArray = Database::Postgre()->SelectResultArray($query);

			foreach($columnArray AS $column)
				if(isset($resultArray[$column->BackgroundName()]))
					$resultColumnArray[$column->Priority()] = $column->RowsColumn($resultArray[$column->BackgroundName()]);
				else
					$resultColumnArray[$column->Priority()] = $column->RowsColumn(NULL);
		}

		return $resultColumnArray;
	}

	public function InsertRow(Array $inputColumnArray)
	{
		$privilege = $this->SelectPrivilege('insert');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$columnArray = $this->SelectColumnArray();

		if($this->hasNameTable)
			unset($columnArray[1]);

		Database::Postgre()->Begin();

		$query = '
INSERT INTO "' . $this->schema . '"."' . $this->backgroundName . '" (id';

		foreach($columnArray AS $column)
		{
			if($column->backgroundName() == 'Language')
				$this->hasLanguage = false;

			if((($column->ColumnType() == 'dropdown') OR ($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'date') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float')) AND (isset($inputColumnArray[$column->Priority()])))
				$query .= ',
		"' . $column->backgroundName() . '"';
		}

		if($this->hasLanguage)
			$query .= ',
		"Language"';

		$query .= ')
	VALUES (DEFAULT';

		foreach($columnArray AS $column)
			if((($column->ColumnType() == 'dropdown') OR ($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'date') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float')) AND (isset($inputColumnArray[$column->Priority()])))
				if($column->ColumnType() == 'dropdown')
					$query .= ',
			' . $inputColumnArray[$column->Priority()]->id();
				elseif(($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'date') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float'))
					$query .= ',
			' . Database::Postgre()->SelectInput($inputColumnArray[$column->Priority()]);

		if($this->hasLanguage)
			$query .= ', ' . Pertyler_Role::Select()->DefaultLanguage()->id();

		$query .= ')
	RETURNING id';

		try
		{
			$id = Database::Postgre()->SelectResult($query);

			if($this->hasNameTable)
			{
				$query = '
INSERT INTO "' . $this->schema . '"."' . $this->uniqueName() . '_name" ("' . $this->uniqueName() . '",
		id,
		"Language",
		"Name"';

				if($this->hasNameTableAlternativeColumn)
						$query .= ',
		"Alternative"';

				$query .= ')
	VALUES (' . $id . ',
			DEFAULT,
			' . Pertyler_Role::Select()->DefaultLanguage()->id() . ',
			' . Database::Postgre()->SelectInput($inputColumnArray[1]);

				if($this->hasNameTableAlternativeColumn)
					$query .= ',
			FALSE';

				$query .= ')';

				Database::Postgre()->SelectResultResource($query);
			}
		}
		catch(PostgreException $e)
		{
			Database::Postgre()->Rollback();
		
			throw new Exception('Wrong SQL for inserting into table "' . $this . '"');
		}

		Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege,
				$id);

		Database::Postgre()->Commit();

		return $this->SelectRow($id);
	}

	public function UpdateRow(Pertyler_Table_Row $row,
			Array $inputColumnArray)
	{
		$privilege = $this->SelectPrivilege('update');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$changed = false;

		$columnArray = $this->SelectRowsColumnArray($row);

		Database::Postgre()->Begin();

		if($this->hasNameTable)
		{
			if((isset($inputColumnArray[1])) AND ($columnArray[1]->Value() != $inputColumnArray[1]))
			{
				$query = '
UPDATE "' . $this->schema . '"."' . $this->uniqueName() . '_name"
	SET "Name" = ' . Database::Postgre()->SelectInput($inputColumnArray[1]) . '
	WHERE "' . $this->uniqueName() . '" = ' . Database::Postgre()->SelectInput($row->id()) . '
			AND "Language" = ' . Pertyler_Role::Select()->DefaultLanguage()->id();

				if($this->hasNameTableAlternativeColumn)
					$query .= '
			AND "Alternative" = false';

				try
				{
					Database::Postgre()->SelectResultResource($query);
				}
				catch(PostgreException $e)
				{
					Database::Postgre()->Rollback();
				
					throw new Exception('Wrong SQL for update table "' . $this . '"');
				}

				$row->ChangeName($inputColumnArray[1]);

				$changed = true;
			}

			unset($columnArray[1]);
		}

		$id = 0;

		$query = '
UPDATE "' . $this->schema . '"."' . $this->backgroundName() . '"
	SET ';

		foreach($columnArray AS $column)
			if($column->ColumnType() == 'dropdown')
			{
				if(isset($inputColumnArray[$column->Priority()]))
				{
					if((!$column->Value()) OR ($column->Value()->id() != $inputColumnArray[$column->Priority()]->id()))
					{
						if($id++)
							$query .= ',
			';

						$query .= '"' . $column->backgroundName() . '" = ' . $inputColumnArray[$column->Priority()]->id();
					}
				}
				elseif($column->Value())
				{
					if($id++)
						$query .= ',
			';

					$query .= '"' . $column->backgroundName() . '" = NULL';
				}
			}
			elseif((($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float')) AND ($column->Value() != $inputColumnArray[$column->Priority()]))
			{
				if($id++)
					$query .= ',
			';

				$query .= '"' . $column->backgroundName() . '" = ' . Database::Postgre()->SelectInput($inputColumnArray[$column->Priority()]);
			}
			elseif(($column->ColumnType() == 'date') AND (((!$column->Value()) AND ($inputColumnArray[$column->Priority()]))
OR (($column->Value()) AND ($inputColumnArray[$column->Priority()]) AND ($column->Value() != $inputColumnArray[$column->Priority()]))
OR (($column->Value()) AND (!$inputColumnArray[$column->Priority()]))))
			{
				if($id++)
					$query .= ',
			';

				$query .= '"' . $column->backgroundName() . '" = ' . Database::Postgre()->SelectInput($inputColumnArray[$column->Priority()]);
			}

		$query .= '
	WHERE id = \'' . $row->id() . '\'';

		if($id)
		{
			try
			{
				Database::Postgre()->SelectResultResource($query);
			}
			catch(DatabaseException $e)
			{
				Database::Postgre()->Rollback();
			
				throw new Exception('Wrong SQL for update table "' . $this . '"');
			}

			$changed = true;
		}

		if($changed)
			Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege,
					$row->id());

		Database::Postgre()->Commit();

		return $changed;
	}

	public function UpdateRowAsApproved(Pertyler_Table_Row $row)
	{
		$privilege = $this->SelectPrivilege('approve');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege, $row->id());
	}

	public function UpdateRowAsJoinedIntoGroup(Pertyler_Table_Row $row, Pertyler_Row_Column $column, Pertyler_Table_Row $joinedRow)
	{
		$privilege = $this->SelectPrivilege('update');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$joinedRowColumnArray = $joinedRow->SelectGroupColumnArray();

		$joinedRowColumn = $joinedRowColumnArray[$column->Priority()];

		Database::Postgre()->Begin();

		try
		{
			if($joinedRowColumn->Value())
				Database::Postgre()->SelectResultArray('
UPDATE "' . $this->schema . '"."' . $this->backgroundName() . '"
	SET "' . $column->backgroundName() . '" = ' . $joinedRowColumn->Value() . '
	WHERE id = \'' . $row->id() . '\'');
			else
			{
				$group = Database::Postgre()->SelectResult('SELECT nextval(\'"' . $this->schema . '"."' . $this->uniqueName() . '_' . $column->backgroundName() . '"\'::regclass)');

				Database::Postgre()->SelectResultResource('
UPDATE "' . $this->schema . '"."' . $this->backgroundName() . '"
	SET "' . $column->backgroundName() . '" = ' . $group . '
	WHERE id IN (\'' . $row->id() . '\', \'' . $joinedRow->id() . '\')');
			}
		}
		catch(DatabaseException $e)
		{
			Database::Postgre()->Rollback();
		
			throw new Exception('Wrong SQL for update table "' . $this->uniqueName() . '"');
		}

		Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege,
				$row->id());

		Database::Postgre()->Commit();

		return true;
	}

	public function UpdateRowAsRemovedFromGroup(Pertyler_Table_Row $row,
			Pertyler_Row_Column $column)
	{
		$privilege = $this->SelectPrivilege('update');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		Database::Postgre()->Begin();

		try
		{
			if(count($row->SelectGroupRowArray($column)) <= 2)
				Database::Postgre()->SelectResultArray('
UPDATE "' . $this->schema . '"."' . $this->backgroundName() . '"
	SET "' . $column->backgroundName() . '" = NULL
	WHERE "' . $column->backgroundName() . '" = \'' . $column->Value() . '\'');
			else
				Database::Postgre()->SelectResultResource('
UPDATE "' . $this->schema . '"."' . $this->backgroundName() . '"
	SET "' . $column->backgroundName() . '" = NULL
	WHERE id = \'' . $row->id() . '\'');
		}
		catch(DatabaseException $e)
		{
			Database::Postgre()->Rollback();
		
			throw new Exception('Wrong SQL for update table "' . $this->uniqueName() . '"');
		}

		Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege,
				$row->id());

		Database::Postgre()->Commit();

		return true;
	}

	public function DeleteRow(Pertyler_Table_Row $row)
	{
		$privilege = $this->SelectPrivilege('delete');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		if($this->NameTable())
		{
			$columnArray = $row->SelectColumnArray();

			if(($columnArray[2]->backgroundName() == 'Alternative') AND (!$columnArray[2]->Value()))
				throw new Exception('Primary name cannot deleted');
		}

		Database::Postgre()->Begin();

		try
		{
			if($this->hasNameTable)
				Database::Postgre()->SelectResultResource('
DELETE FROM "' . $this->schema . '"."' . $this->uniqueName() . '_name"
	WHERE "' . $this->uniqueName() . '" = ' . $row->id());

			Database::Postgre()->SelectResultResource('
DELETE FROM "' . $this->schema . '"."' . $this->backgroundName() . '" "' . $this->uniqueName() . '"
	WHERE "' . $this->uniqueName() . '".id = \'' . $row->id() . '\'');
		}
		catch(DatabaseException $e)
		{
			Database::Postgre()->Rollback();

			throw new Exception('Row cannot deleted');
		}

		Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege,
				$row->id());

		Database::Postgre()->Commit();

		return true;
	}
}

class Pertyler_Row_Table extends Pertyler_Table
{
	private $parent;

	protected function __construct(Pertyler_Table_Row $parent,
			$parent_id,
			$id,
			$name,
			$shortName,
			$backgroundName,
			$schema,
			$hasNameTable,
			$hasNameTableAlternativeColumn,
			$hasLanguage,
			$hasParentsName,
			$tableType)
	{
		$this->parent = $parent;

		parent::__construct($parent_id,
				$id,
				$name,
				$shortName,
				$backgroundName,
				$schema,
				$hasNameTable,
				$hasNameTableAlternativeColumn,
				$hasLanguage,
				$hasParentsName,
				$tableType);
	}

	public function __toString()
	{
		if($this->shortName)
			return $this->shortName;
		else
			return $this->name;
	}

	public function Parent()
	{
		return $this->parent;
	}

	protected function nameSelectSql()
	{
		$query = '';

		$prefix = '';

		if(!$this->SelectColumn(1))
			throw new Exception('Table ' . $this . ' has no name column.');

		$table = $this;

		while(($table->SelectColumn(1)->RelatedTable()) AND ($table->SelectColumn(1)->RelatedTable()->SelectColumn(1)))
		{
			$table = $table->SelectColumn(1)->RelatedTable();

			$prefix = 'Name_';

			if($table->hasParentsName)
				$query .= $table->addParentsNameSql($prefix);
		}

		if($table->hasNameTable)
			$query .= '"' . $prefix . $table->uniqueName() . 'Name"."Name"';
		else
		{
			$query .= '"' . $prefix . $table->uniqueName() . '"."' . $table->SelectColumn(1)->backgroundName() . '"::text';
		}

		return $query;
	}

	public function SelectRowArray()
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$query = $this->selectClauseSql();

		$query .= $this->fromClauseSql(false);

		$query .= $this->whereClauseSql();

		$query .= '
		AND "' . $this->uniqueName() . '"."' . $this->SelectParentTable()->uniqueName() . '"::text = ' . $this->Parent()->id() . '::text';

		$query .= '
ORDER BY "Name"';

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);
		}
		catch(DatabaseException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this . '"');
		}

		$rowArray = array();

		if($resultArrayArray)
			foreach($resultArrayArray AS $resultArray)
				$rowArray[] = new Pertyler_Table_Row($this, $resultArray['id'], $resultArray['Name']);

		return $rowArray;
	}

	public function SelectUnpublishedRowArray()
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		if((!$this->SelectColumn(2)) OR ($this->SelectColumn(2)->backgroundName() != 'Unpublished'))
			throw new Exception('No unpublished column');

		$rowArray = array();

		$query = $this->selectClauseSql();

		$query .= $this->fromClauseSql(false);

		$query .= '
WHERE "' . $this->uniqueName() . '"."Unpublished" = true
		AND ' . $this->whereClauseLanguageCheckSql() . '
		AND "' . $this->uniqueName() . '"."' . $this->SelectParentTable()->uniqueName() . '" = ' . $this->parent->id();

		$query .= '
ORDER BY "Name"';

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($query);
		}
		catch(DatabaseException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this . '"');
		}

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name'],
					NULL);

		return $rowArray;
	}

	public function SelectUnapprovedRowArray()
	{
		$privilege = $this->SelectPrivilege('select');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		try
		{
			$resultArrayArray = Database::Postgre()->SelectResultArrayArray($this->selectClauseSql() . $this->fromClauseSql(false) . '
	WHERE ' . $this->whereClauseLanguageCheckSql() . '
		AND "' . $this->uniqueName() . '"."' . $this->SelectParentTable()->uniqueName() . '" = ' . $this->parent->id() . '
		AND "' . $this->uniqueName() . '".id::text !~ \'[^0-9]\'
			AND NOT EXISTS(SELECT true
					FROM pertyler."Table_ChangeLog"
					WHERE id  = (SELECT max(id)
							FROM pertyler."Table_ChangeLog"
							WHERE "Table" = ' . $this->id . '
									AND "Row" = "' . $this->uniqueName() . '".id::int)
							AND "PrivilegeType" = 4)
	ORDER BY EXISTS(SELECT true
			FROM pertyler."Table_ChangeLog"
			WHERE "Table" = ' . $this->id . '
					AND "Row" = "' . $this->uniqueName() . '".id::int
					AND "PrivilegeType" = 4),
			"Name"');
		}
		catch(DatabaseException $e)
		{
			throw new Exception('Wrong SQL for table "' . $this . '"');
		}

		if(!$resultArrayArray)
			return array();

		foreach($resultArrayArray AS $resultArray)
			$rowArray[] = new Pertyler_Table_Row($this,
					$resultArray['id'],
					$resultArray['Name'],
					NULL);

		return $rowArray;
	}

	public function InsertRow(Array $inputColumnArray)
	{
		$privilege = $this->SelectPrivilege('insert');

		if(!$privilege)
			throw new Exception('No ' . $privilage . ' privilege');

		if(!Pertyler_Role::Select()->SelectRolePrivilege($privilege))
			throw new Pertyler_RolePrivilege_Exception($privilege);

		$columnArray = $this->SelectColumnArray();

		if($this->hasNameTable)
			unset($columnArray[1]);

		Database::Postgre()->Begin();

		$query = '
INSERT INTO "' . $this->schema . '"."' . $this->backgroundName . '"
("' . $this->SelectParentTable()->uniqueName() . '", id';

		foreach($columnArray AS $column)
		{
			if($column->backgroundName() == 'Language')
				$this->hasLanguage = false;

			if((($column->ColumnType() == 'dropdown') OR ($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'date') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float')) AND (isset($inputColumnArray[$column->Priority()])))
				$query .= ', "' . $column->backgroundName() . '"';
		}

		if($this->hasLanguage)
			$query .= ', "Language"';

		$query .= ')
	VALUES (' . $this->Parent()->id() . ',
			DEFAULT';

		foreach($columnArray AS $column)
			if((($column->ColumnType() == 'dropdown') OR ($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'date') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float')) AND (isset($inputColumnArray[$column->Priority()])))
				if($column->ColumnType() == 'dropdown')
					$query .= ', ' . $inputColumnArray[$column->Priority()]->id();
				elseif(($column->ColumnType() == 'integer') OR ($column->ColumnType() == 'string') OR ($column->ColumnType() == 'text') OR ($column->ColumnType() == 'paragraph') OR ($column->ColumnType() == 'date') OR ($column->ColumnType() == 'boolean') OR ($column->ColumnType() == 'float'))
					$query .= ', ' . Database::Postgre()->SelectInput($inputColumnArray[$column->Priority()]);

		if($this->hasLanguage)
			$query .= ', ' . Pertyler_Role::Select()->DefaultLanguage()->id();

		$query .= ')
	RETURNING id';

		try
		{
			$id = Database::Postgre()->SelectResult($query);

			if($this->hasNameTable)
			{
				$query = '
INSERT INTO "' . $this->schema . '"."' . $this . '_name" ("' . $this . '",
		id,
		"Language",
		"Name"';

				if($this->hasNameTableAlternativeColumn)
						$query .= ',
		"Alternative"';

				$query .= ')
	VALUES (' . $id . ',
			DEFAULT,
			' . Pertyler_Role::Select()->DefaultLanguage()->id() . ',
			' . Database::Postgre()->SelectInput($inputColumnArray[1]);

				if($this->hasNameTableAlternativeColumn)
						$query .= ',
			FALSE';

				$query .= ')';

				Database::Postgre()->SelectResultResource($query);
			}
		}
		catch(DatabaseException $e)
		{
			Database::Postgre()->Rollback();
		
			throw new Exception('Wrong SQL for inserting into table "' . $this->uniqueName() . '"');
		}

		Pertyler_Role_Moderator::Select()->InsertChangeLog($privilege,
				$id);

		Database::Postgre()->Commit();

		return $this->SelectRow($id);
	}
}

class Pertyler_Table_Column
{
	private $parent, $priority, $name, $backgroundName, $columnType, $relatedTable;

	public function __construct(Pertyler_Table $parent, $priority, $name, $backgroundName, $columnType, $relatedTable)
	{
		$this->parent = $parent;

		$this->priority = $priority;

		$this->name = $name;

		$this->backgroundName = $backgroundName;

		$this->columnType = $columnType;

		$this->relatedTable = $relatedTable;
	}

	public function Parent()
	{
		return $this->parent;
	}

	public function Priority()
	{
		return $this->priority;
	}

	public function __toString()
	{
		return $this->name;
	}

	public function backgroundName()
	{
		return $this->backgroundName;
	}

	public function ColumnType()
	{
		return $this->columnType;
	}

	public function RelatedTable()
	{
		return $this->relatedTable;
	}

	public function RowsColumn($value)
	{
		return new Pertyler_Row_Column($this->parent,
				$this->priority,
				$this->name,
				$this->backgroundName,
				$this->columnType,
				$this->relatedTable,
				$value);
	}
}

class Pertyler_Table_Privilege
{
	private $parent,
			$id,
			$name,
			$language;

	public function __construct(Pertyler_Table $parent,
			$id,
			$name,
			Pertyler_Language $language)
	{
		$this->parent = $parent;

		$this->id = $id;

		$this->name = $name;

		$this->language = $language;
	}

	public function id()
	{
		return $this->id;
	}

	public function __toString()
	{
		return $this->name;
	}

	public function Language()
	{
		return $this->language;
	}
}

class Pertyler_Table_Row
{
	private $parent,
			$id,
			$name,
			$unpublished;

	public function __construct(Pertyler_Table $parent,
			$id,
			$name,
			$unpublished = false)
	{
		$this->parent = $parent;

		$this->id = $id;

		$this->name = $name;

		$this->unpublished = $unpublished;
	}

	public function id()
	{
		return $this->id;
	}

	public function __ToString()
	{
		return $this->name;
	}

	public function Unpublished()
	{
		return $this->unpublished;
	}

	public function ChangeName($name)
	{
		return $this->name = $name;
	}

	public function VirtualRow()
	{
		return !is_numeric($this->id);
	}

	private $lastChangeLogTime,
			$lastChangeLogModeratorName,
			$approved;

	public function SelectLastChangeLog()
	{
		if(!$this->parent->SelectPrivilege('approve'))
			throw new Exception('No approve privilege');

		if(!isset($this->lastChangeLogTime))
		{
			$resultArray = Database::Postgre()->SelectResultArray('
SELECT "ChangeLog"."Time",
		"Moderator"."Name" "Moderator_Name",
		"ApprovePrivilege".id::boolean "Approved"
	FROM pertyler."Moderator_ChangeLog" "ChangeLog"
		JOIN pertyler."Role_Moderator" "Moderator" ON "Moderator".id = "ChangeLog"."Moderator"
		LEFT JOIN pertyler."Table_Privilege" "ApprovePrivilege" ON "ApprovePrivilege".id = "ChangeLog"."Privilege"
				AND "ApprovePrivilege"."PrivilegeType" = ' . 4 . '
	WHERE "ChangeLog".id  = (SELECT max(id)
				FROM pertyler."Table_ChangeLog"
				WHERE "Table" = ' . $this->parent->id() . '
						AND "Row" = ' . $this->id() . ')');

			if($resultArray)
			{
				$this->lastChangeLogTime = $resultArray['Time'];

				$this->lastChangeLogModeratorName = $resultArray['Moderator_Name'];

				$this->approved = $resultArray['Approved'];
			}
			else
			{
				$this->lastChangeLogTime = false;

				$this->lastChangeLogModeratorName = false;

				$this->approved = false;
			}
		}

		return (boolean) $this->lastChangeLogTime;
	}

	public function SelectLastChangeLogTime()
	{
		if(!isset($this->lastChangeLogTime))
			$this->SelectLastChangeLog();

		return $this->lastChangeLogTime;
	}

	public function SelectLastChangeLogModeratorName()
	{
		if(!isset($this->lastChangeLogTime))
			$this->SelectLastChangeLog();

		return $this->lastChangeLogModeratorName;
	}

	public function SelectApproved()
	{
		if(!isset($this->lastChangeLogTime))
			$this->SelectLastChangeLog();

		return (boolean) $this->approved;
	}

	public function SelectTableArray()
	{
		return $this->parent->SelectRowsTableArray($this);
	}

	public function SelectTable($id)
	{
		$table = Pertyler_Table::Select($id);

		if($table)
			return $table->RowsTable($this);
		else
			throw new Exception('Table cannot found.');
	}

	public function SelectParentRow()
	{
		return $this->parent->SelectRowsParentRow($this);
	}

	public function SelectColumnArray()
	{
		return $this->parent->SelectRowsColumnArray($this);
	}

	public function SelectGroupColumnArray()
	{
		return $this->parent->SelectRowsGroupColumnArray($this);
	}

	public function SelectGroupRowArray(Pertyler_Row_Column $column)
	{
		return $this->parent->SelectRowsGroupRowArray($this,
				$column);
	}

	public function Update(Array $inputColumnArray)
	{
		if($this->parent->UpdateRow($this,
				$inputColumnArray))
		{
			$this->lastChangeLogTime = new Date;

			$this->lastChangeLogModeratorName = Pertyler_Role_Moderator::Select();
			
			$this->approved = NULL;

			return true;
		}
		else
			return false;
	}

	public function UpdateAsJoinedIntoGroup(Pertyler_Row_Column $column,
			Pertyler_Table_Row $joinedRow)
	{
		$this->parent->UpdateRowAsJoinedIntoGroup($this,
				$column,
				$joinedRow);

		$this->lastChangeLogTime = new Date;

		$this->lastChangeLogModeratorName = Pertyler_Role_Moderator::Select();
		
		$this->approved = NULL;
	}

	public function UpdateAsRemovedFromGroup(Pertyler_Row_Column $column)
	{
		$this->parent->UpdateRowAsRemovedFromGroup($this, $column);

		$this->lastChangeLogTime = new Date;

		$this->lastChangeLogModeratorName = Pertyler_Role_Moderator::Select();
		
		$this->approved = NULL;
	}

	public function UpdateAsApproved()
	{
		$this->parent->UpdateRowAsApproved($this);

		$this->lastChangeLogTime = new Date;

		$this->lastChangeLogModeratorName = Pertyler_Role_Moderator::Select();

		$this->approved = true;
		
		return true;
	}

	public function Delete()
	{
		return $this->parent->DeleteRow($this);
	}
}

class Pertyler_Row_Column extends Pertyler_Table_Column
{
	private $value;

	public function __construct(Pertyler_Table $parent,
			$priority,
			$name,
			$backgroundName,
			$columnType,
			$relatedTable,
			$value)
	{
		parent::__construct($parent,
				$priority,
				$name,
				$backgroundName,
				$columnType,
				$relatedTable);

		$this->value = $value;
	}

	public function Value()
	{
		if($this->value)
			return $this->value;
		else
			return '';
	}
}